package com.multicampus.simplewiki.parser;

import java.util.HashMap;
import java.util.Map;

import com.multicampus.simplewiki.parser.replacer.Replacer;

public class WikiParser {
	private Replacer linktoHtmlReplacer;
	private Replacer camelCaseReplacer;
	
	private Map<Integer, String> representationData;
	Map<String, Markup> formatSymbolTable = new HashMap<String, Markup>();
	Map<String, Markup> emphasisSymbolTable = new HashMap<String, Markup>();

	public void setLinktoHtmlReplacer(Replacer linktoHtmlReplacer) {
		this.linktoHtmlReplacer = linktoHtmlReplacer;
	}

	public void setCamelCaseReplacer(Replacer camelCaseReplacer) {
		this.camelCaseReplacer = camelCaseReplacer;
	}

	public WikiParser() {
		representationData = new HashMap<Integer, String>();

		formatSymbolTable.put("h1.", new Markup("<h1>", "</h1>"));
		formatSymbolTable.put("h2.", new Markup("<h2>", "</h2>"));
		formatSymbolTable.put("h3.", new Markup("<h3>", "</h3>"));
		formatSymbolTable.put("h4.", new Markup("<h4>", "</h4>"));
		formatSymbolTable.put("h5.", new Markup("<h5>", "</h5>"));

		emphasisSymbolTable.put("*", new Markup("<b>", "</b>"));
		emphasisSymbolTable.put("_", new Markup("<i>", "</i>"));
		emphasisSymbolTable.put("+", new Markup("<u>", "</u>"));
		emphasisSymbolTable.put("-", new Markup("<strike>", "</strike>"));
	}

	public String parse(String wikiString) throws Exception {
		representationData.clear();
		WikiTokenizer tokenizer = new WikiTokenizer(wikiString);
		WikiToken token;

		while ((token = tokenizer.nextToken()) != null) {
			switch (token.getType()) {
			// normal text
			case 0:
				representationData.put(token.getOrder(), camelCaseReplacer.replace(token.getContents()));
				break;
			// emphasis
			case 1:
				treatEmphasis(token, tokenizer);
				tokenizer.setCurrent(token);
				break;
			// format
			case 2:
				treatFormat(token, tokenizer);
				break;
			// newline
			case 3:
				representationData.put(token.getOrder(), "<br/>");
				break;
			// Link
			case 4:
				treatLink(token, tokenizer);
				break;
				// escape
			case 6:
				representationData.put(token.getOrder(), "");
				break;
			}
		}

		StringBuffer parsedData = new StringBuffer();
		
		for (int i = 1; i <= representationData.size(); i++) {
			parsedData.append(representationData.get(i));
		}

		return parsedData.toString();
	}

	private void treatLink(WikiToken token, WikiTokenizer tokenizer) {
		WikiToken prevToken = tokenizer.getPrevToken(token.getOrder() - 1);
		WikiToken nextToken;

		if (!token.getContents().equals("["))
			return;

		if (representationData.get(token.getOrder()) != null) {
			return;
		}

		if (prevToken == null || prevToken.getType() != 6) {
			while ((nextToken = tokenizer.nextToken()) != null) {
				if (nextToken.getType() == 3) {
					tokenizer.setCurrent(token);
					break;
				}

				if (nextToken.getType() == 4 && nextToken.getContents().equals("]")) {
					StringBuffer buffer = new StringBuffer();
					for(int i = token.getOrder() ; i <= nextToken.getOrder() ; i++){
						buffer.append(tokenizer.getPrevToken(i).getContents());
					}
					
					representationData.put(token.getOrder(), linktoHtmlReplacer.replace(buffer.toString()));
					
					for(int i = token.getOrder() + 1; i <= nextToken.getOrder() ; i++ )
						representationData.put(i, "");
					
					return;
				}
			}
		}
		
		representationData.put(token.getOrder(), token.getContents());
	}

	private void treatFormat(WikiToken token, WikiTokenizer tokenizer) {
		WikiToken prevToken = tokenizer.getPrevToken(token.getOrder() - 1);
		if (prevToken == null || prevToken.getType() == 3) {
			Markup markup = formatSymbolTable.get(token.getContents());
			representationData.put(token.getOrder(), markup.getStartTag());

			while ((token = tokenizer.nextToken()) != null) {
				if (token.getType() == 3) {
					representationData.put(token.getOrder(), markup.getEndTag() + "<br/>");
					break;
				} else
					representationData.put(token.getOrder(), token.getContents());
			}

			if (token == null)
				representationData.put(tokenizer.getTokenCount(), markup.getEndTag());
		} else
			representationData.put(token.getOrder(), token.getContents());
	}

	private void treatEmphasis(WikiToken token, WikiTokenizer tokenizer) {
		WikiToken prevToken = tokenizer.getPrevToken(token.getOrder() - 1);
		WikiToken nextToken;

		if (representationData.get(token.getOrder()) != null) {
			return;
		}

		if (prevToken == null || prevToken.getType() != 6) {
			while ((nextToken = tokenizer.nextToken()) != null) {
				if (nextToken.getType() == 3) {
					break;
				}

				if (nextToken.getType() == 1 && nextToken.getContents().equals(token.getContents())) {
					prevToken = tokenizer.getPrevToken(nextToken.getOrder() - 1);

					if (prevToken.getType() != 6) {
						representationData.put(token.getOrder(), emphasisSymbolTable.get(token.getContents()).getStartTag());
						representationData.put(nextToken.getOrder(), emphasisSymbolTable.get(nextToken.getContents()).getEndTag());
						return;
					}
				}
			}
		}

		representationData.put(token.getOrder(), token.getContents());
	}
}
